package io.gitee.zhangbinhub.admin.service

import com.mybatisflex.core.query.QueryWrapper
import io.gitee.zhangbinhub.acp.boot.exceptions.WebException
import io.gitee.zhangbinhub.admin.base.OauthBaseService
import io.gitee.zhangbinhub.admin.entity.Menu
import io.gitee.zhangbinhub.admin.entity.RoleMenu
import io.gitee.zhangbinhub.admin.entity.User
import io.gitee.zhangbinhub.admin.po.MenuPo
import io.gitee.zhangbinhub.admin.repo.MenuRepository
import io.gitee.zhangbinhub.admin.repo.RoleMenuRepository
import io.gitee.zhangbinhub.admin.repo.RoleRepository
import io.gitee.zhangbinhub.admin.repo.UserRepository
import io.gitee.zhangbinhub.admin.vo.MenuVo
import org.noear.solon.annotation.Component
import org.noear.solon.annotation.Inject
import org.noear.solon.data.annotation.Transaction

@Component
open class MenuService(
    @Inject userRepository: UserRepository,
    private val roleRepository: RoleRepository,
    private val menuRepository: MenuRepository,
    private val roleMenuRepository: RoleMenuRepository
) : OauthBaseService(userRepository) {

    fun getAllMenuList(): MutableList<Menu> =
        menuRepository.selectListByQuery(QueryWrapper.create().orderBy(Menu::sort).asc()).let {
            val map: MutableMap<String, Menu> = mutableMapOf()
            it.forEach { item ->
                map[item.id] = item
            }
            formatToTreeList(map)
        }

    private fun sortMenuList(menuList: MutableList<Menu>): MutableList<Menu> =
        menuList.let { list ->
            list.forEach { menu ->
                if (menu.children.isNotEmpty()) {
                    sortMenuList(menu.children)
                }
            }
            menuList.apply {
                this.sortBy { it.sort }
            }
        }

    @Throws(WebException::class)
    fun getMenuList(appId: String, loginNo: String): MutableList<Menu> =
        (getUserInfoByLoginNo(loginNo) ?: throw WebException("无法获取当前用户信息")).let {
            val menuIds: MutableSet<String> = mutableSetOf()
            it.roleSet.filter { role -> role.appId == appId }
                .flatMap { item -> item.menuSet }
                .filter { item ->
                    if (menuIds.contains(item.id) || !item.enabled) {
                        false
                    } else {
                        menuIds.add(item.id)
                        true
                    }
                }
                .filter { menu -> menu.appId == appId }
                .let { menu ->
                    val map: MutableMap<String, Menu> = mutableMapOf()
                    menu.forEach { item ->
                        map[item.id] = item
                    }
                    map
                }.let { map ->
                    val result: MutableList<Menu> = mutableListOf()
                    map.forEach { (_, menu) ->
                        if (map.containsKey(menu.parentId)) {
                            map.getValue(menu.parentId).children.add(menu)
                        } else if (menu.parentId == menu.appId) {
                            result.add(menu)
                        }
                    }
                    sortMenuList(result)
                }
        }

    fun getMenuListByAppId(appId: String): MutableList<Menu> =
        menuRepository.selectListByQuery(QueryWrapper.create().where(Menu::appId).eq(appId)).let {
            val map: MutableMap<String, Menu> = mutableMapOf()
            it.forEach { item ->
                map[item.id] = item
            }
            sortMenuList(formatToTreeList(map))
        }

    @Throws(WebException::class)
    private fun doSave(userInfo: User, menu: Menu, menuPo: MenuPo): Menu =
        (if (menuPo.roleIds.isNotEmpty()) {
            roleRepository.selectListByIds(menuPo.roleIds)
        } else {
            mutableListOf()
        }).let { roleSetPo ->
            if (validateModifyRoleSet(userInfo, menuPo.appId!!, menu.roleSet, roleSetPo)) {
                menu.copy(
                    path = menuPo.path,
                    enabled = menuPo.enabled,
                    iconType = menuPo.iconType,
                    name = menuPo.name!!,
                    openType = menuPo.openType,
                    sort = menuPo.sort
                ).apply {
                    parentId = menuPo.parentId!!
                }.also {
                    menuRepository.insertOrUpdate(it)
                }.also {
                    roleMenuRepository.deleteByQuery(QueryWrapper.create().where(RoleMenu::menuId).eq(it.id))
                    if (menuPo.roleIds.isNotEmpty()) {
                        roleMenuRepository.insertBatch(menuPo.roleIds.map { roleId ->
                            RoleMenu(roleId = roleId, menuId = it.id)
                        })
                    }
                }
            } else {
                throw WebException("不合法的操作，不允许修改更高级别的角色列表！")
            }
        }

    @Transaction
    @Throws(WebException::class)
    open fun doCreate(loginNo: String, menuPo: MenuPo): Menu =
        getUserInfoByLoginNo(loginNo)?.let { userInfo ->
            doSave(
                userInfo, Menu(
                    appId = menuPo.appId!!,
                    covert = true
                ), menuPo
            )
        } ?: throw WebException("无法获取当前用户信息")

    @Transaction
    @Throws(WebException::class)
    open fun doDelete(idList: MutableList<String>) {
        menuRepository.selectListByQuery(QueryWrapper.create().where(Menu::parentId).`in`(idList)).apply {
            if (this.isNotEmpty()) {
                throw WebException("存在下级菜单，不允许删除")
            }
        }
        roleMenuRepository.deleteByQuery(QueryWrapper.create().where(RoleMenu::menuId).`in`(idList))
        menuRepository.deleteByQuery(QueryWrapper.create().where(Menu::id).`in`(idList).and(Menu::covert).eq(true))
    }

    @Transaction
    @Throws(WebException::class)
    open fun doUpdate(loginNo: String, menuPo: MenuPo): Menu =
        getUserInfoByLoginNo(loginNo)?.let { userInfo ->
            doSave(userInfo, menuRepository.selectOneWithRelationsById(menuPo.id!!), menuPo)
        } ?: throw WebException("无法获取当前用户信息")

    @Throws(WebException::class)
    fun getMenuInfo(menuId: String): MenuVo =
        menuRepository.selectOneWithRelationsById(menuId).let { item ->
            MenuVo(
                id = item.id,
                appId = item.appId,
                enabled = item.enabled,
                iconType = item.iconType,
                name = item.name,
                openType = item.openType,
                parentId = item.parentId,
                path = item.path,
                sort = item.sort,
                roleIds = item.roleSet.map { it.id }.toMutableList()
            )
        }
}
